home *** CD-ROM | disk | FTP | other *** search
/ Aminet 23 / Aminet 23 (1998)(GTI - Schatztruhe)[!][Feb 1998].iso / Aminet / text / edit / Smartindent.lha / Smartindent / Source / semantics_c.c < prev    next >
C/C++ Source or Header  |  1997-12-14  |  20KB  |  1,147 lines

  1. /*(( "Kopf" */
  2. /* -----------------------------------------------------------------------------
  3.  
  4.    $Id: semantics_c.c,v 1.5 1997/07/17 00:24:10 mshopf Exp mshopf $
  5.  
  6.    GoldED API client, ©1995 Matthias Hopf.
  7.    Compiled with SasC.
  8.  
  9.    C and C++ semantics parser
  10.  
  11.    ------------------------------------------------------------------------------
  12. */
  13.  
  14. /*)) */
  15. #include "semantics.h"
  16.  
  17. /*
  18.  * uncomment this if you want recursive comments like:   / * / *  xxx  * /  * /
  19.  */
  20. // #define RECURSIVE_COMMENTS
  21.  
  22. /*(( "Private prototypes" */
  23.  
  24. int isBlockKeyword (char *);
  25. int isAttrKeyword (char *);
  26.  
  27. FUNC (DoStd);
  28.  
  29. FUNC (BLOCK);
  30. FUNC (BLOCK_ONE);
  31. FUNC (CMD_STD);
  32. FUNC (CMD_EQ);
  33. FUNC (ROUND_BRACK);
  34. FUNC (SQUARE_BRACK);
  35. FUNC (COMMENT);
  36. FUNC (CPPCOMMENT);
  37. FUNC (PRAEPROC);
  38. FUNC (STRING_DOUBLE);
  39. FUNC (STRING_SINGLE);
  40.  
  41. /*)) */
  42. /*(( "CIndent ()" */
  43.  
  44. /****** Main routine ******/
  45. void    Indent_C (sc_t *c)
  46. {
  47.     BLOCK (c, 0);
  48.     if (c->CurrentLine <= c->EndIndent)
  49.     Error (c, UNMATCHED_BRACE_ERROR);
  50.     debug (Dbug, ("\n\n"));             // Do it always...
  51. }
  52.  
  53. /*)) */
  54. /*(( "IsWord_C ()" */
  55.  
  56. int IsWord_C (sc_t *c, char *buf, int len, int maxlen)
  57. {
  58.     char *t;
  59.     switch (len) {
  60.     case 0:
  61.     case 1:
  62.     return MATCH_TRUE;
  63.  
  64.     case 2:
  65.     switch (buf [0]) {
  66.     case '/':
  67.         switch (buf [1]) {
  68.         case '/':
  69.         case '*':
  70.         return MATCH_EXACT;
  71.         }
  72.         break;
  73.     case '*':
  74.         switch (buf [1]) {
  75.         case '/':
  76.         return MATCH_EXACT;
  77.         case '*':                   /* for  / *  ... ** ... * / multiline comments */
  78.         if (len < maxlen && buf [2] == '/') /* mind the '* /' */
  79.             return MATCH_FALSE;
  80.         else
  81.             return MATCH_EXACT;
  82.         }
  83.         break;
  84.     case '.':
  85.         switch (buf [1]) {
  86.         case '.':
  87.         if (len < maxlen && buf [2] == '.') /* potential ... */
  88.             return MATCH_TRUE;
  89.         return MATCH_FALSE;     /* we have no '..' in C */
  90.         }
  91.         break;
  92.     case '+':
  93.         switch (buf [1]) {
  94.         case '+':
  95.         return MATCH_EXACT;
  96.         }
  97.         break;
  98.     case '-':
  99.         switch (buf [1]) {
  100.         case '>':
  101.         case '-':
  102.         return MATCH_EXACT;
  103.         }
  104.         break;
  105.     case ':':
  106.         switch (buf [1]) {
  107.         case ':':
  108.         return MATCH_EXACT;
  109.         }
  110.         break;
  111.     case '&':
  112.         switch (buf [1]) {
  113.         case '&':
  114.         return MATCH_TRUE;      /* &&= possible */
  115.         }
  116.         break;
  117.     case '|':
  118.         switch (buf [1]) {
  119.         case '|':
  120.         return MATCH_TRUE;      /* ||= possible */
  121.         }
  122.         break;
  123.     case '^':
  124.         switch (buf [1]) {
  125.         case '^':
  126.         return MATCH_TRUE;      /* ^^= possible */
  127.         }
  128.         break;
  129.     case '>':
  130.         switch (buf [1]) {
  131.         case '>':
  132.         return MATCH_TRUE;      /* >>= possible */
  133.         }
  134.         break;
  135.     case '<':
  136.         switch (buf [1]) {
  137.         case '<':
  138.         return MATCH_TRUE;      /* <<= possible */
  139.         }
  140.         break;
  141.     }
  142.     switch (buf [1]) {
  143.     case '=':                       /* ==, !=, +=, *=, ...*/
  144.         switch (buf [0]) {
  145.         case '!':
  146.         case '=':
  147.         case '>':
  148.         case '<':
  149.         case '+':
  150.         case '-':
  151.         case '*':
  152.         case '/':
  153.         case '%':
  154.         case '|':
  155.         case '&':
  156.         case '^':
  157.         return MATCH_EXACT;
  158.         }
  159.         return MATCH_FALSE;
  160.     }
  161.     break;
  162.  
  163.     case 3:
  164.     switch (buf [2]) {
  165.     case '.':                       /* printf (char *format, ...) */
  166.         if (buf [1] == '.' && buf [2] == '.')
  167.         return MATCH_EXACT;
  168.         return MATCH_FALSE;
  169.     case '=':                       /* >>=, &&=, ...*/
  170.         if (buf [0] == buf [1])
  171.         switch (buf [0]) {
  172.         case '>':
  173.         case '<':
  174.         case '&':
  175.         case '|':
  176.         case '^':
  177.             return MATCH_EXACT;
  178.         }
  179.         return MATCH_FALSE;
  180.  
  181.     }
  182.     break;
  183.     }
  184.  
  185.     if (isalpha (buf [0]) || buf[0] == '_')
  186.     {
  187.     for (t = buf+1; t < & buf [len]; t++)
  188.         if (!isalnum (*t) && *t != '_')
  189.         return MATCH_FALSE;
  190.     return MATCH_TRUE;
  191.     }
  192.     if (isdigit (buf [0]) || buf [0] == '.' || buf [0] == '-' || buf [0] == '+')
  193.     {
  194.     for (t = buf+1; t < & buf [len]; t++)
  195.         if (!isdigit (*t) && *t != '.' && *t != 'e')
  196.         return MATCH_FALSE;
  197.     return MATCH_TRUE;
  198.     }
  199.     return MATCH_FALSE;
  200. }
  201.  
  202.  
  203. /*)) */
  204. /*(( "KeyPress_C ()" */
  205.  
  206. void KeyPress_C (sc_t *c, int key)
  207. {
  208.     int Line   = c->Edit->Line;
  209.     int Col    = c->Edit->Column;
  210.     int Len, i;
  211.     char *Text;
  212.     int StartLine = -1, EndLine = -1;
  213.  
  214.     if (c->Edit->CurrentBuffer)
  215.     {
  216.     Text = c->Edit->CurrentBuffer;
  217.     Len  = c->Edit->CurrentLen;
  218.     }
  219.     else
  220.     {
  221.     Text = c->Edit->TextNodes [Line] .Text;
  222.     Len  = c->Edit->TextNodes [Line] .Len;
  223.     }
  224.  
  225.  
  226.     switch (key) {
  227.  
  228.     case '*':
  229.         /* only when first char in line or before or after '/' or '*' */
  230.     for (i = 0; i < Col; i++)
  231.         if (! isspace (Text [i]))
  232.         break;
  233.     if (i == Col)
  234.     {
  235.         StartLine = EndLine = Line;
  236.         break;
  237.     }
  238.         /* no break!*/
  239.  
  240.     case '/':
  241.         /* only before or or after '/' or '*' */
  242.     if (Col != 0)
  243.     {
  244.         switch (Text [Col-1]) {
  245.         case '/':
  246.         case '*':
  247.         StartLine = EndLine = Line;
  248.         }
  249.     }
  250.     if (Col < Len-1)
  251.     {
  252.         switch (Text [Col+1]) {
  253.         case '/':
  254.         case '*':
  255.         StartLine = EndLine = Line;
  256.         }
  257.     }
  258.     break;
  259.  
  260.     case ':':
  261.     case '{':
  262.     case '}':
  263.     StartLine = EndLine = Line;
  264.     break;
  265.  
  266.     case '\n':
  267.     case '\15':
  268.     StartLine = Line > 0 ? Line-1 : 0;
  269.     EndLine   = Line;
  270.     }
  271.  
  272.     if (StartLine >= 0)
  273.     {
  274.     InitIndent (c, StartLine, EndLine, MODE_LINE | MODE_CURSOR);
  275.     Indent_C (c);
  276.     CleanupIndent (c);
  277.     }
  278. }
  279.  
  280.  
  281. /*)) */
  282. /*(( "Semantics structure" */
  283.  
  284. /****** Method structure ******/
  285. struct Semantic C_Sem =
  286. {
  287.     "c",
  288.     "C and C++ semantics parser",
  289.     "V0.1 ©1995 Matthias Hopf",
  290.     IsWord_C,
  291.     KeyPress_C,
  292.     Indent_C,
  293.     { 4, 0, -2, 2, 1, 4, 40 }
  294. } ;
  295.  
  296. /*)) */
  297.  
  298. /*(( "isBlockKeyword ()" */
  299.  
  300. /****** Test if a character string is a block keyword ******/
  301. int     isBlockKeyword (char *t)
  302. {
  303.     /* 'if'  needs special treatment due to 'else' */
  304.     /* 'try' needs special treatment due to 'catch' */
  305.     return (streq (t, "if") || streq (t, "else") || streq (t, "do")
  306.         || streq (t, "for") || streq (t, "while")
  307.         || streq (t, "switch") || streq (t, "catch"));
  308. }
  309.  
  310. /*)) */
  311. /*(( "isAttrKeyword ()" */
  312.  
  313. /****** Test if a character string is a attribute keyword ******/
  314. /* int, char, etc. are not listed here, because they are types! */
  315. int     isAttrKeyword (char *t)
  316. {
  317.     return (   streq (t, "enum")     || streq (t, "struct")    || streq (t, "union")
  318.            || streq (t, "auto")     || streq (t, "static")    || streq (t, "extern")
  319.            || streq (t, "const")    || streq (t, "unsigned")  || streq (t, "signed")
  320.            || streq (t, "register") || streq (t, "volatile")
  321.            || streq (t, "template") || streq (t, "friend")    || streq (t, "class")
  322.            || streq (t, "inline")   || streq (t, "virtual")   || streq (t, "asm"));
  323. }
  324.  
  325. /*)) */
  326. /*(( "DoStd ()" */
  327.  
  328. /****** Handling of standard situations ******/
  329. FUNC (DoStd)
  330. {
  331.     debug (D_PARSER, ("DoStd[%s](%ld)\t", W, I));
  332.     dcheck;
  333.  
  334.     switch (W[0]) {
  335.     case '#':
  336.     if (C == 0)
  337.         CALL (PRAEPROC, 0);
  338.     else
  339.         ERROR (SYNTAX_ERROR);
  340.     return;
  341.  
  342.     case '(':
  343.     UPFIRST (I);
  344.     CALL (ROUND_BRACK, N);
  345.     INDENT  (I);                    /* Try to indent ')' */
  346.     return;
  347.  
  348.     case '[':
  349.     UPFIRST (I);
  350.     CALL (SQUARE_BRACK, N);
  351.     INDENT  (I);                    /* Try to indent ')' */
  352.     return;
  353.  
  354.     case '"':
  355.     UPFIRST (I);
  356.     CALL (STRING_DOUBLE, C+1);
  357.     return;
  358.  
  359.     case '\'':
  360.     UPFIRST (I);
  361.     CALL (STRING_SINGLE, C+1);
  362.     return;
  363.  
  364.     case '/':
  365.     switch (W[1]) {
  366.     case '/':
  367.         if (BOL)
  368.         {
  369.         if (C == 0)
  370.             INDENT (0);
  371.         else
  372.             INDENT (I+CONFIG.Comment_Level);
  373.         }
  374.         else
  375.         INDENT (CONFIG.LineComment_Abs);
  376.         CALL (CPPCOMMENT, 0);       /* new indentation level is irrelelvant (one line comment) */
  377.         return;
  378.     case '*':
  379.         if (BOL)
  380.         {
  381.         if (C == 0)
  382.             INDENT (0);
  383.         else
  384.             INDENT (I+CONFIG.Comment_Level);
  385.         }
  386.         else
  387.         INDENT (CONFIG.LineComment_Abs);
  388.         CALL (COMMENT, C);
  389.         return;
  390.         /* no default */
  391.     }
  392.         /* no break */
  393.  
  394.     default:
  395.     ERROR (INTERNAL_ERROR);
  396.     return;
  397.     }
  398. }
  399.  
  400. /*)) */
  401. /*(( "BLOCK ()" */
  402.  
  403. FUNC (BLOCK)
  404. {
  405.     debug (D_PARSER, ("BLOCK(%ld)\t", I));
  406.     dcheck;
  407.  
  408.     SETBOL;                             /* Always indent first thing after '{' */
  409.  
  410.     DOGET
  411.     {
  412.     c->BlockIndent = I;
  413.  
  414.     switch (W[0]) {                 // no test for length == 1 !
  415.     case '(':
  416.     case '[':
  417.     case '#':
  418.         CALL1 (DoStd);
  419.         break;
  420.  
  421.     case '{':
  422.         INDENT (I + CONFIG.Brace_Level);
  423.         CALL   (BLOCK, I + CONFIG.Block_Level);
  424.         INDENT (I + CONFIG.Brace_Level);
  425.         break;
  426.  
  427.     case '}':
  428.         return;
  429.  
  430.     case ';':
  431.         UPFIRST (I);
  432.         break;
  433.  
  434.     case ',':
  435.         CALL (CMD_STD, I + CONFIG.Cont_Level);
  436.         break;
  437.  
  438.     case '"':
  439.     case '\'':
  440.         UPFIRST (I);
  441.         UNGET;
  442.         CALL (CMD_STD, C+1);
  443.         break;
  444.  
  445.     case '=':                       /* struct bla foo = { }; */
  446.         if (W[1] == '\0')
  447.         {
  448.         UPFIRST (I);
  449.         CALL (CMD_EQ, N);
  450.         break;
  451.         }
  452.         // no break!!!, e.g. >=
  453.     case ')':
  454.     case ']':
  455.     case ':':
  456.     case '?':
  457.         ERROR (SYNTAX_ERROR);
  458.  
  459.     case '\\':
  460.         if (EOL && W[1] == '\0')
  461.         break;
  462.         ERROR (SYNTAX_ERROR);
  463.  
  464.     case '/':                       // Comment
  465.         if (W[1] != 0)
  466.         {
  467.         CALL1 (DoStd);
  468.         break;
  469.         }
  470.         // no break!!!
  471.     default:
  472.         UPFIRST (I);
  473.         if (streq (W, "else"))
  474.         {
  475.         PEEK;
  476.         if (streq (P, "if"))
  477.         {
  478.             GET;
  479.             INDENT (I);
  480.         }
  481.         CALL (BLOCK_ONE, I + CONFIG.Block_Level);
  482.         }
  483.         else if (isBlockKeyword (W)) // else is already handled
  484.         CALL (BLOCK_ONE, I + CONFIG.Block_Level);
  485.  
  486.         else if (! isAttrKeyword (W))
  487.         {
  488.         if (I == 0)             /* First level indentation will stay 0 (function definitions) */
  489.             CALL (CMD_STD, 0);
  490.         else
  491.         {
  492.             if (EOL)
  493.             CALL (CMD_STD, I + CONFIG.Cont_Level);
  494.             else
  495.             CALL (CMD_STD, N);
  496.         }
  497.         PEEK;                   /* have we encountered a label? (special case!) */
  498.         if (P[0] == ':' && P[1] == '\0')
  499.         {
  500.             /* the current line is indented as a label if, and only if
  501.              * a) it was only preceeded by *one* alphanummeric word
  502.              * b) a 'case' is found as the first word in the line.
  503.              * public:, private:, etc. are handled by case a).
  504.              */
  505.             char *Text;
  506.             int  Len, i;
  507.  
  508.             GetText (c, &Text, &Len);/* get first word in line */
  509.             for (i = 0; i <= Len; i++)
  510.             if (! isspace (Text[i]))
  511.                 break;
  512.             if (i+5 <= Len)     /* check for 'case' */
  513.             {
  514.             if (strncmp (&Text[i], "case", 4) == 0)
  515.             {
  516.                 if (isspace (Text[i+4]))
  517.                 i = -1;
  518.             }
  519.             }
  520.             if (i != -1)
  521.             {                   /* else check whether ':' comes immedeately after the first word */
  522.             for (    ; i < Len; i++)
  523.                 if (! isalnum (Text[i]))
  524.                 break;
  525.             for (    ; i < Len; i++)
  526.                 if (! isspace (Text[i]))
  527.                 break;
  528.             if (Text[i] == ':')
  529.                 i = -1;
  530.             }
  531.  
  532.             if (i == -1)
  533.             {                   /* Ok, we *really* got a label */
  534.             GET;
  535.             GetText (c, &Text, &Len);
  536.             for (i = 0; i <= C; i++)
  537.                 if (! isspace (Text [i]))
  538.                 break;
  539.             Move (c, i, c->BlockIndent + CONFIG.Label_Level);
  540.             I = c->BlockIndent;
  541.             break;
  542.             }
  543.             
  544.             GET;                /* otherwise this was some C++ statement */
  545.         }
  546.         }
  547.  
  548.     }
  549.     }
  550. }
  551.  
  552. /*)) */
  553. /*(( "BLOCK_ONE ()" */
  554.  
  555. FUNC (BLOCK_ONE)
  556. {
  557.     int Initial;
  558.  
  559.     debug (D_PARSER, ("BLOCK_ONE(%ld)\t", I));
  560.     dcheck;
  561.  
  562.     Initial = I - CONFIG.Block_Level;
  563.  
  564.     SETBOL;                             /* Always indent first thing after '{' */
  565.  
  566.     DOGET
  567.     {
  568.     switch (W[0]) {                 // no test for length == 1 !
  569.     case '(':
  570.     case '[':
  571.     case '#':
  572.         CALL1 (DoStd);
  573.         break;
  574.  
  575.     case '{':
  576.         INDENT (Initial + CONFIG.Brace_Level);
  577.         CALL   (BLOCK, Initial + CONFIG.Block_Level);
  578.         INDENT (Initial + CONFIG.Brace_Level);
  579.         return;
  580.  
  581.     case ';':
  582.         UPFIRST (I);
  583.         return;
  584.  
  585.     case ',':
  586.         CALL (CMD_STD, I + CONFIG.Cont_Level);
  587.         break;
  588.  
  589.     case '}':
  590.     case '=':                       // incl. ==
  591.     case ')':
  592.     case ']':
  593.     case ':':
  594.     case '?':
  595.     case '"':
  596.     case '\'':
  597.         ERROR (SYNTAX_ERROR);
  598.  
  599.     case '\\':
  600.         if (EOL && W[1] == '\0')
  601.         break;
  602.         ERROR (SYNTAX_ERROR);
  603.  
  604.     case '/':                       // Comment
  605.         if (W[1] != 0)
  606.         {
  607.         CALL1 (DoStd);
  608.         break;
  609.         }
  610.         // no break!!!
  611.     default:
  612.         UPFIRST (I);
  613.         if (streq (W, "if"))
  614.         {
  615.         for (;;)
  616.         {
  617.             CALL (BLOCK_ONE, I + CONFIG.Block_Level);
  618.             PEEK;
  619.             if (streq (P, "else"))
  620.             {
  621.             GET;
  622.             UPFIRST (I);
  623.             PEEK;
  624.             if (streq (P, "if"))
  625.             {
  626.                 GET;
  627.                 INDENT (I);
  628.                 continue;
  629.             }
  630.             CALL (BLOCK_ONE, I + CONFIG.Block_Level);
  631.             }
  632.             return;
  633.         }
  634.         }
  635.         else if (isBlockKeyword (W)) // if is already handled
  636.         CALL (BLOCK_ONE, I + CONFIG.Block_Level);
  637.  
  638.         else if (! isAttrKeyword (W))
  639.         {
  640.         if (I == 0)             /* First level indentation will stay 0 (function definitions) */
  641.             CALL (CMD_STD, 0);
  642.         else
  643.         {
  644.             if (EOL)
  645.             CALL (CMD_STD, I + CONFIG.Cont_Level);
  646.             else
  647.             CALL (CMD_STD, N);
  648.         }
  649.         }
  650.     }
  651.     }
  652. }
  653.  
  654. /*)) */
  655. /*(( "BLOCK_SET ()" */
  656.  
  657. FUNC (BLOCK_SET)
  658. {
  659.     debug (D_PARSER, ("BLOCK_SET(%ld)\t", I));
  660.     dcheck;
  661.  
  662.  
  663.     if (EOL)                            /* Only indent first thing after '{' when we are in a new line */
  664.     SETBOL;
  665.     else
  666.     I = N;                          /* Otherwise we take the next char position as intendation */
  667.  
  668.     DOGET
  669.     {
  670.     c->BlockIndent = I;
  671.  
  672.     switch (W[0]) {                 // no test for length == 1 !
  673.     case '(':
  674.     case '[':
  675.     case '#':
  676.         CALL1 (DoStd);
  677.         break;
  678.  
  679.     case '{':
  680.         INDENT (I + CONFIG.Brace_Level);
  681.         CALL   (BLOCK_SET, I + CONFIG.Block_Level);
  682.         INDENT (I + CONFIG.Brace_Level);
  683.         break;
  684.  
  685.     case '}':
  686.         return;
  687.  
  688.     case ',':
  689.         UPFIRST (I);
  690.         break;
  691.  
  692.     case '"':
  693.     case '\'':
  694.         UPFIRST (I);
  695.         UNGET;
  696.         CALL (CMD_STD, N);
  697.         break;
  698.  
  699.     case ';':
  700.     case '=':                       // incl. ==
  701.     case ')':
  702.     case ']':
  703.     case ':':
  704.     case '?':
  705.         ERROR (SYNTAX_ERROR);
  706.  
  707.     case '\\':
  708.         if (EOL && W[1] == '\0')
  709.         break;
  710.         ERROR (SYNTAX_ERROR);
  711.  
  712.     case '/':                       // Comment
  713.         if (W[1] != 0)
  714.         {
  715.         CALL1 (DoStd);
  716.         break;
  717.         }
  718.         // no break!!!
  719.     default:
  720.         UPFIRST (I);
  721.         if (isBlockKeyword (W))
  722.         ERROR (SUSPICIOUS_ERROR);
  723.         else if (! isAttrKeyword (W))
  724.         {
  725.         if (EOL)
  726.             CALL (CMD_STD, I + CONFIG.Cont_Level);
  727.         else
  728.             CALL (CMD_STD, N);
  729.         }
  730.     }
  731.     }
  732. }
  733.  
  734. /*)) */
  735. /*(( "ROUND_BRACK ()" */
  736.  
  737. FUNC (ROUND_BRACK)
  738. {
  739.     debug (D_PARSER, ("ROUND_BRACK(%ld)\t", I));
  740.     dcheck;
  741.  
  742.     DOGET
  743.     {
  744.     switch (W[0]) {                 // no test for length == 1 !
  745.     case '(':
  746.     case '[':
  747.     case '#':
  748.     case '\'':
  749.     case '"':
  750.         CALL1 (DoStd);
  751.         break;
  752.  
  753.     case ')':
  754.         return;                     /* Indented by DoStd */
  755.  
  756.     case ';':                       /* for (;;) */
  757.         UPFIRST (I);
  758.         break;
  759.  
  760.     case '{':
  761.     case '}':
  762.     case ']':
  763.         ERROR (SYNTAX_ERROR);
  764.  
  765.     case '\\':
  766.         if (W[1] == 0)
  767.         break;
  768.         ERROR (SYNTAX_ERROR);
  769.  
  770.     case '/':                       // Comment
  771.         if (W[1] != 0)
  772.         {
  773.         CALL1 (DoStd);
  774.         break;
  775.         }
  776.         // no break!!!
  777.     default:
  778.         UPFIRST (I);
  779.         if (isBlockKeyword (W))
  780.         ERROR (SUSPICIOUS_ERROR);
  781.         else
  782.         CALL1 (CMD_STD);
  783.     }
  784.     }
  785. }
  786.  
  787. /*)) */
  788. /*(( "SQUARE_BRACK ()" */
  789.  
  790. FUNC (SQUARE_BRACK)
  791. {
  792.     debug (D_PARSER, ("SQUARE_BRACK(%ld)\t", I));
  793.     dcheck;
  794.  
  795.     DOGET
  796.     {
  797.     switch (W[0]) {                 // no test for length == 1 !
  798.     case '(':
  799.     case '[':
  800.     case '#':
  801.     case '\'':
  802.     case '"':
  803.         CALL1 (DoStd);
  804.         break;
  805.  
  806.     case ']':
  807.         return;                     /* Indented by DoStd */
  808.  
  809.     case ';':                       /* for (;;) */
  810.     case '{':
  811.     case '}':
  812.     case ')':
  813.     case '=':
  814.     case ':':
  815.     case '?':
  816.         ERROR (SYNTAX_ERROR);
  817.  
  818.     case '\\':
  819.         if (W[1] == 0)
  820.         break;
  821.         ERROR (SYNTAX_ERROR);
  822.  
  823.     case '/':                       // Comment
  824.         if (W[1] != 0)
  825.         {
  826.         CALL1 (DoStd);
  827.         break;
  828.         }
  829.         // no break!!!
  830.     default:
  831.         UPFIRST (I);
  832.         if (isBlockKeyword (W))
  833.         ERROR (SUSPICIOUS_ERROR);
  834.         else
  835.         CALL1 (CMD_STD);
  836.     }
  837.     }
  838. }
  839.  
  840. /*)) */
  841. /*(( "CMD_STD ()" */
  842.  
  843. FUNC (CMD_STD)
  844. {
  845.     char real [4];
  846.  
  847.     debug (D_PARSER, ("CMD_STD(%ld)\t", I));
  848.     dcheck;
  849.  
  850.     DOGET
  851.     {
  852.     strncpy (real, W, 3);
  853.     if (W[1] == '=' || (W[1] != '\0' && W[2] == '='))
  854.         W[0] = '=';
  855.  
  856.     switch (W[0]) {                 // no test for length == 1 !
  857.     case '(':
  858.     case '[':
  859.     case '"':
  860.     case '\'':
  861.     case '#':
  862.         CALL1 (DoStd);
  863.         break;
  864.  
  865.     case '}':
  866.     case ',':
  867.     case ';':
  868.     case ')':
  869.     case ']':
  870.         UNGET;
  871.         return;
  872.  
  873.     case '=':                       // = / == / += etc.
  874.         if (real[1] == '=' && real [2] == '\0')
  875.         {
  876.         if (real[0] == '=' || real[0] == '<' || real [0] == '>' || real [0] == '!')
  877.         {
  878.             UPFIRST (I);
  879.             break;
  880.         }
  881.         }
  882.         // no break!!!
  883.     case '?':
  884.         UPFIRST (I);
  885.         CALL (CMD_EQ, N);
  886.         break;;
  887.  
  888.     case ':':                       // : / ::
  889.         if (W[1] == '\0')
  890.         {
  891.         UNGET;
  892.         return;
  893.         }
  894.         UPFIRST (I);
  895.         break;
  896.  
  897.     case '{':
  898.         I = c->BlockIndent;
  899.         INDENT (I + CONFIG.Brace_Level);
  900.         CALL   (BLOCK, I + CONFIG.Block_Level);
  901.         INDENT (I + CONFIG.Brace_Level);
  902.         return;
  903.  
  904.     case '\\':
  905.         if (W[1] == 0)
  906.         break;
  907.         ERROR (SYNTAX_ERROR);
  908.  
  909.     case '/':                       // Comment
  910.         if (W[1] != 0)
  911.         {
  912.         CALL1 (DoStd);
  913.         break;
  914.         }
  915.         // no break!!!
  916.     default:
  917.         UPFIRST (I);
  918.         if (isBlockKeyword (W))
  919.         ERROR (SUSPICIOUS_ERROR);
  920.     }
  921.     }
  922. }
  923.  
  924. /*)) */
  925. /*(( "CMD_EQ ()" */
  926.  
  927. FUNC (CMD_EQ)
  928. {
  929.     debug (D_PARSER, ("CMD_EQ(%ld)\t", I));
  930.     dcheck;
  931.  
  932.     DOGET
  933.     {
  934.     switch (W[0]) {                 // no test for length == 1 !
  935.     case '(':
  936.     case '[':
  937.     case '"':
  938.     case '\'':
  939.     case '#':
  940.         CALL1 (DoStd);
  941.         break;
  942.  
  943.     case '}':
  944.     case ',':
  945.     case ';':
  946.     case ')':
  947.     case ']':
  948.         UNGET;
  949.         return;
  950.  
  951.     case '?':
  952.         UPFIRST (I);
  953.         CALL (CMD_EQ, N);
  954.         break;;
  955.  
  956.     case '{':
  957.         I = c->BlockIndent;
  958.         INDENT (I + CONFIG.Brace_Level);
  959.         CALL   (BLOCK_SET, I + CONFIG.Block_Level);
  960.         INDENT (I + CONFIG.Brace_Level);
  961.         return;
  962.  
  963.     case '\\':
  964.         if (W[1] == 0)
  965.         break;
  966.         ERROR (SYNTAX_ERROR);
  967.  
  968.     case '/':                       // Comment
  969.         if (W[1] != 0)
  970.         {
  971.         CALL1 (DoStd);
  972.         break;
  973.         }
  974.         // no break!!!
  975.     default:
  976.         UPFIRST (I);
  977.         if (isBlockKeyword (W))
  978.         ERROR (SUSPICIOUS_ERROR);
  979.     }
  980.     }
  981. }
  982.  
  983. /*)) */
  984. /*(( "COMMENT ()" */
  985.  
  986. FUNC (COMMENT)
  987. {
  988.     int Intro, Extro;
  989.  
  990.     debug (D_PARSER, ("COMMENT(%ld)\t", I));
  991.     dcheck;
  992.  
  993.     Intro  = I;                         /*        Position of the '/ *' */
  994.     Extro  = I;                         /* Future Position of the '* /' */
  995.     I      = N;
  996.  
  997.     DOGET
  998.     {
  999.     if (streq (W, "*/"))
  1000.     {
  1001.         UPDATE (Extro);
  1002.         return;
  1003.     }
  1004. #ifdef RECURSIVE_COMMENTS
  1005.     else if (streq (W, "/*"))       /* We have recursive comments */
  1006.         CALL1 (DoStd);
  1007. #endif
  1008.     else if (BOL)
  1009.     {
  1010.         if (W[0] == '*' && W[1] == '\0')
  1011.         {
  1012.         Extro = Intro + 1;
  1013.         UPDATE (Intro + 1);
  1014.         }
  1015.         else if (streq (W, "**"))
  1016.         {
  1017.         Extro = Intro;
  1018.         UPDATE (Intro);
  1019.         }
  1020.         else
  1021.         {
  1022.         Extro = Intro;
  1023.         UPDATE (I);
  1024.         }
  1025.     }
  1026.     }
  1027. }
  1028.  
  1029. /*)) */
  1030. /*(( "CPPCOMMENT ()" */
  1031.  
  1032. FUNC (CPPCOMMENT)
  1033. {
  1034.     debug (D_PARSER, ("CPPCOMMENT(%ld)\t", I));
  1035.     dcheck;
  1036.  
  1037.     GET;
  1038.     INDENT (I + 3);
  1039.     UNGET;
  1040.  
  1041.     DOGET
  1042.     {
  1043.     if (EOL)
  1044.         return;
  1045.     }
  1046. }
  1047.  
  1048. /*)) */
  1049. /*(( "PRAEPROC ()" */
  1050.  
  1051. FUNC (PRAEPROC)
  1052. {
  1053.     char *Text;
  1054.     int  Len, i;
  1055.  
  1056.     debug (D_PARSER, ("PRAEPROC(%ld)\t", I));
  1057.     dcheck;
  1058.  
  1059.     if (EOL)
  1060.     ERROR (PRAEPROCESSOR_ERROR);
  1061.  
  1062.     GET;                                /* get preproc directive (e.g. define) */
  1063.     if (EOL)
  1064.     return;
  1065.  
  1066.     /* get name including parameters */
  1067.     GetText (c, &Text, &Len);
  1068.     for (i = N; i < Len; i++)
  1069.     if (isspace (Text[i]) || Text[i] == '\\')
  1070.         break;
  1071.     for (     ; i < Len; i++)
  1072.     if (! isspace (Text[i]))
  1073.         break;
  1074.  
  1075.     C = i;
  1076.     if (Text[i] == '\\')
  1077.     SET (I + CONFIG.Cont_Level);
  1078.     else
  1079.     SET (i);
  1080.  
  1081.     DOGET
  1082.     {
  1083.     UPFIRST (I);
  1084.     if (! streq (W, "\\") && EOL)
  1085.         return;
  1086.     }
  1087. }
  1088.  
  1089. /*)) */
  1090. /*(( "STRING_SINGLE ()" */
  1091.  
  1092. FUNC (STRING_SINGLE)
  1093. {
  1094.     debug (D_PARSER, ("STRING_SINGLE(%ld)\t", I));
  1095.     dcheck;
  1096.  
  1097.     if (EOL)
  1098.     ERROR (STRING_TERMINATION_ERROR);
  1099.  
  1100.     DOGET
  1101.     {
  1102.     if (W[1] == 0)
  1103.     {
  1104.         switch (W[0]) {
  1105.         case '\'':
  1106.         return;
  1107.  
  1108.         case '\\':
  1109.         GET;                    /* skip next char (may be ' or \ ) */
  1110.         }
  1111.     }
  1112.     if (EOL)
  1113.         ERROR (STRING_TERMINATION_ERROR);
  1114.     }
  1115. }
  1116.  
  1117. /*)) */
  1118. /*(( "STRING_DOUBLE ()" */
  1119.  
  1120. FUNC (STRING_DOUBLE)
  1121. {
  1122.     debug (D_PARSER, ("STRING_DOUBLE(%ld)\t", I));
  1123.     dcheck;
  1124.  
  1125.     if (EOL)
  1126.     ERROR (STRING_TERMINATION_ERROR);
  1127.  
  1128.     DOGET
  1129.     {
  1130.     if (W[1] == 0)
  1131.     {
  1132.         switch (W[0]) {
  1133.         case '"':
  1134.         return;
  1135.  
  1136.         case '\\':
  1137.         GET;                    /* skip next char (may be " or \ ) */
  1138.         }
  1139.     }
  1140.     if (EOL)
  1141.         ERROR (STRING_TERMINATION_ERROR);
  1142.     }
  1143. }
  1144.  
  1145. /*)) */
  1146.  
  1147.